package com.osgix.common.orm.ibatis;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.util.Assert;

import com.osgix.common.exception.UnimplementedException;
import com.osgix.common.utils.PropertyUtils;

public abstract class AbsEntityDao<T extends BaseEntity, PK extends Serializable> extends IbatisDaoSupport implements
		EntityDao<T, PK> {
	protected final Log log = LogFactory.getLog(getClass());

	private SqlSessionFactory sqlSessionFactory;

	private IbatisTemplate ibatisTemplate;

	public IbatisTemplate getIbatisTemplate() {
		return ibatisTemplate;
	}

	public void setIbatisTemplate(IbatisTemplate ibatisTemplate) {
		this.ibatisTemplate = ibatisTemplate;
	}

	protected void checkDaoConfig() throws IllegalArgumentException {
		Assert.notNull(sqlSessionFactory, "sqlSessionFactory must be not null");
	}

	public SqlSessionFactory getSqlSessionFactory() {
		return sqlSessionFactory;
	}

	public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
		this.sqlSessionFactory = sqlSessionFactory;
	}

	public T getById(PK primaryKey) {
		T object = (T) getIbatisTemplate().selectOne(getFindByPrimaryKeyStatement(), primaryKey);
		return object;
	}

	public T findUnique(Object param) {
		return (T) getIbatisTemplate().selectOne(getFindStatement(), param);
	}

	public Long getCount() {
		return (Long) getIbatisTemplate().selectOne(getCountStatement());
	}

	public void deleteById(PK id) {
		getIbatisTemplate().delete(getDeleteStatement(), id);
	}

	@Override
	public void delete(T entity) {
		deleteById((PK) entity.getPkValue());
	}

	public void save(T entity) {
		prepareObjectForSaveOrUpdate(entity);
		getIbatisTemplate().insert(getInsertStatement(), entity);
	}

	public void update(T entity) {
		prepareObjectForSaveOrUpdate(entity);
		getIbatisTemplate().update(getUpdateStatement(), entity);
	}

	public List<T> findAll(String... sortConditions) {
		if (sortConditions == null) {
			return getIbatisTemplate().selectList(getFindAllStatement());
		} else {
			Map<String, Object> param = new HashMap<String, Object>();
			StringBuffer sb = new StringBuffer();
			for (int i = 0; i < sortConditions.length; i++) {
				sb.append(sortConditions[i]);
				if (sortConditions.length - 1 != i)
					sb.append(",");
			}
			param.put("sortColumns", sb.toString());
			return getIbatisTemplate().selectList(getFindAllStatement(), param);
		}
	}
	
	public Page<T> findPage(PageRequest pageRequest) {
		return pageQuery(getIbatisTemplate(), getFindPageStatement(), getCountStatement(), pageRequest);
	}

	@SuppressWarnings("unchecked")
	private static Page pageQuery(IbatisTemplate ibatisTemplate, String statementName, String countStatementName,
			PageRequest pageRequest) {
		Number totalCount = (Number) ibatisTemplate.selectOne(countStatementName, pageRequest);
		if (totalCount == null || totalCount.longValue() <= 0) {
			return new Page(pageRequest, 0);
		}
		Page page = new Page(pageRequest, totalCount.intValue());
		// 其它分页参数
		// 与getIbatisTemplate().queryForList(statementName, parameterObject)配合使用
		Map filters = new HashMap();
		filters.put("offset", page.getFirstResult());
		filters.put("pageSize", page.getPageSize());
		filters.put("lastRows", page.getFirstResult() + page.getPageSize());
		filters.put("sortColumns", pageRequest.getSortColumns());
		Map parameterObject = PropertyUtils.describe(pageRequest);
		filters.putAll(parameterObject);
		List list = ibatisTemplate.selectList(statementName, filters, page.getFirstResult(), page.getPageSize());
		page.setResult(list);
		return page;
	}

	/**
	 * 用于子类覆盖,在insert,update之前调用
	 */
	protected void prepareObjectForSaveOrUpdate(T o) {
	}

	public String getIbatisMapperNamesapce() {
		throw new UnimplementedException("Not yet implement");
	}

	public String getFindByPrimaryKeyStatement() {
		return getIbatisMapperNamesapce() + ".getById";
	}

	public String getInsertStatement() {
		return getIbatisMapperNamesapce() + ".insert";
	}

	public String getUpdateStatement() {
		return getIbatisMapperNamesapce() + ".update";
	}

	public String getDeleteStatement() {
		return getIbatisMapperNamesapce() + ".delete";
	}
	
	private String getFindStatement() {
		return getIbatisMapperNamesapce() + ".find";
	}
	
	private String getFindPageStatement() {
		return getIbatisMapperNamesapce() + ".findPage";
	}

	private String getFindAllStatement() {
		return getIbatisMapperNamesapce() + ".findAll";
	}

	public String getCountStatement() {
		return getIbatisMapperNamesapce() + ".count";
	}

	public void flush() {
		// ignore
	}

}
